<template>
  <div class="log-container">
    <el-drawer
      :title="recordTitle"
      :append-to-body="false"
      :visible="recordVisible"
      :size="size"
      :before-close="closeBuildHistory"
      :wrapper-closable="false"
      :modal-append-to-body="false"
    >
      <div v-loading="hisLoading" class="drawer-container">
        <!-- 固定头部区域 -->
        <div class="fixed-header">
          <!-- 复合搜索框 -->
          <el-input
            v-if="!multipleSearch"
            v-model="searchKeyword"
            class="complex-input"
            :placeholder="currentHolderStr"
            size="mini"
            clearable
            :class="{ 'is-invalid': isInvalid }"
            @clear="clearContext"
            @focus="handleTypeChange"
            @blur="validateInput"
          >
            <!-- 前置搜索类型选择 -->
            <template #prepend>
              <el-select
                v-model="searchProp"
                style="width: 100px"
                @change="handleTypeChange"
              >
                <el-option
                  v-for="item in searchPropOptions"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </template>
            <!-- 动态输入区域 -->
            <template #append>
              <!-- 搜索按钮 -->
              <el-button
                type="primary"
                icon="el-icon-search"
                @click="handleSearch"
              />
            </template>
          </el-input>
          <!-- 错误提示 -->
          <span v-if="showErrorMessage" class="error-msg">{{ errorMessage }}</span>

          <!-- 操作时间：日期选择器 -->
          <div v-if="!multipleSearch" class="complex-date-picker">
            <div
              v-for="item in searchOptions"
              :id="item.prop"
              :key="item.prop"
              ref="complexElDivRef"
            >
              <el-date-picker
                v-if="item.type === 2"
                :ref="'datePickerRef-' + item.prop"
                v-model="datePickerVal[item.prop]"
                type="datetimerange"
                :picker-options="item.options"
                range-separator="至"
                start-placeholder="开始日期"
                end-placeholder="结束日期"
                align="right"
                clearable
                style="width: 0"
                value-format="yyyy-MM-dd HH:mm:ss"
                format="yyyy-MM-dd HH:mm:ss"
                @change="handleKeyWordDisplay"
              />

              <!-- 构建结果：下拉选择 -->
              <el-select
                v-else-if="item.type === 3"
                :ref="'selectRef-' + item.prop"
                v-model="searchSelectVal[item.prop]"
                class="complex-select"
                placeholder="请选择结果"
                clearable
                @change="handleKeyWordDisplay"
              >
                <el-option
                  v-for="res in item.options"
                  :key="res.value"
                  :label="res.label"
                  :value="res.value"
                />
              </el-select>
              <el-input
                v-else
                v-model="searchInputVal[item.prop]"
                size="mini"
                clearable
                @input="handleKeyWordDisplay"
              />
            </div>
          </div>
          <el-form
            v-else
            ref="multiFormRef"
            size="mini"
            :model="multiForm"
            :rules="multiRules"
            :label-width="labelWidth"
            class="multi-ruleForm"
          >
            <!-- 第一组始终显示 -->
            <el-form-item
              :label="defSearchOption.label"
              :prop="defSearchOption.prop"
              class="first-item"
            >
              <!-- 条件1的组件 -->
              <component
                :is="getComponentType(defSearchOption)"
                v-model="multiForm[defSearchOption.prop]"
                v-bind="getComponentProps(defSearchOption)"
                class="responsive-input"
              >
                <template v-if="defSearchOption.type === 3">
                  <el-option
                    v-for="option in defSearchOption.options"
                    :key="option.value"
                    :label="option.label"
                    :value="option.value"
                  />
                </template>
              </component>

              <!-- 展开/收起按钮 (放在第一个表单项内) -->
              <el-button
                style="position: absolute;top: -24px;right: 0;"
                type="text"
                :icon="isExpanded ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"
                @click="isExpanded = !isExpanded"
              >
                {{ isExpanded ? '收起' : '展开' }}
              </el-button>
            </el-form-item>

            <!-- 展开区域 -->
            <div v-show="isExpanded" class="expand-area">
              <!-- 其他查询条件 -->
              <el-form-item
                v-for="item in searchOptions.filter(sItem => sItem.prop !== defSearchOption.prop)"
                :key="item.prop"
                :label="item.label"
                :prop="item.prop"
              >
                <component
                  :is="getComponentType(item)"
                  v-model="multiForm[item.prop]"
                  v-bind="getComponentProps(item)"
                >
                  <template v-if="item.type === 3">
                    <el-option
                      v-for="option in item.options"
                      :key="option.value"
                      :label="option.label"
                      :value="option.value"
                    />
                  </template>
                </component>
              </el-form-item>
            </div>

            <!-- 展开状态下的按钮 -->
            <el-form-item class="action-buttons">
              <el-button
                type="primary"
                icon="el-icon-search"
                @click="handleSearch"
              >查询</el-button>
              <el-button
                icon="el-icon-refresh-left"
                @click="resetMultiForm"
              >重置</el-button>
            </el-form-item>
          </el-form>
          <el-divider />
        </div>
        <div class="scroll-content">
          <el-timeline>
            <el-timeline-item
              v-for="(activity, index) in records"
              :key="index"
              :icon="activity.status === 0 ? 'el-icon-video-pause' :
                activity.status === 1 ? 'el-icon-loading'
                : activity.status === 2 ? 'el-icon-success'
                  : activity.status === 3 ? 'el-icon-error' : 'el-icon-time'"
              :color="activity.status === 0 ? '#909399' :
                activity.status === 1 ? '#4ACBEA'
                : activity.status === 2 ? '#0bbd87'
                  : activity.status === 3 ? '#f56c6c': '#e6a23c'"
              :timestamp="activity.timestamp"
              type="primary"
              placement="top"
              size="large"
            >
              <el-card>
                <el-descriptions
                  :title="getDescTitle(activity)"
                  :column="1"
                  border
                  size="mini"
                >
                  <template slot="extra">
                    <el-button type="primary" size="mini" @click="lookForDetail(activity)">查看详情</el-button>
                  </template>
                  <el-descriptions-item
                    v-for="descItem in recordDesc.items || []"
                    :key="descItem.prop"
                    :label="descItem.label"
                  >{{ activity[descItem.prop] }}</el-descriptions-item>
                </el-descriptions>
              </el-card>
            </el-timeline-item>
          </el-timeline>
          <!-- 触发加载的占位元素 -->
          <div ref="loaderRef" class="loader-trigger">{{ loadingText }}</div>
        </div>
      </div>
    </el-drawer>
  </div>
</template>

<script>
export default {
  name: 'LogRecord',
  emits: [
    /**
     * 查看日志详情回调
     */
    'look-for-detail',
    /**
     * 显示最新记录回调
     */
    'show-latest'
  ],
  props: {
    // 是否多条件搜索，默认为false，即每次只能使用一个条件
    multipleSearch: {
      type: Boolean,
      default: false
    },

    size: {
      type: String,
      default: '30%'
    },

    // 日志记录标题
    recordTitle: {
      type: String,
      required: true
    },

    // 是否打开日志记录
    recordVisible: {
      type: Boolean,
      required: true
    },

    /**
     * <ul>
     *   搜索可选项，如：操作时间，操作人，操作结果等，说明：
     *   <li>type为1表示文本框，2表示日期时间选择器，3表示下拉框；<span style="color:red;font-weight: bolder;">（目前只能支持此三种组件）</span></li>
     *   <li>isDefault表示是否默认展示当前条件，默该属性都为false，则设置列表第一个为默认；</li>
     *   <li>时间选择器，可以接受当前时间日期选择器特有的选项参考下表；</li>
     *   <li>下拉框，可以接受下拉框的选项值；</li>
     *   <li>支持规则校验</li>
     * </li>
     * 格式例如:
     * ```[{
     *         label: '操作人',
     *         prop: 'operator',
     *         type: 1,
     *         placeHolder: '请输入操作人名称',
     *         isDefault: true,
     *         rules: [{
     *            required: true, message: "请输入文本", trigger: "blur"
     *         }]
     *       }, {
     *         label: '操作时间',
     *         prop: 'timestamp',
     *         type: 2,
     *         placeHolder: '请选择时间范围',
     *         options: {
     *         shortcuts: [{
     *           text: '最近一周',
     *           onClick(picker) {
     *             const end = new Date()
     *             const start = new Date()
     *             start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
     *             picker.$emit('pick', [start, end])
     *           }
     *         }, {
     *           text: '最近一个月',
     *           onClick(picker) {
     *             const end = new Date()
     *             const start = new Date()
     *             start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
     *             picker.$emit('pick', [start, end])
     *           }
     *         }, {
     *           text: '最近三个月',
     *           onClick(picker) {
     *             const end = new Date()
     *             const start = new Date()
     *             start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
     *             picker.$emit('pick', [start, end])
     *           }
     *         }]
     *       }
     *       }, {
     *         label: '构建结果',
     *         prop: 'buildRes',
     *         type: 3,
     *         placeHolder: '请选择构建结果',
     *         options: [{
     *         label: '构建成功',
     *         value: '2'
     *       }, {
     *         label: '构建失败',
     *         value: '3'
     *       }, {
     *         label: '执行中',
     *         value: '1'
     *       }]
     *   }]
     */
    searchOptions: {
      required: true,
      type: Array
    },

    /**
     * 日志描述信息定义，格式为：
     * ```
     * {
     *  title: '操作人：',
     *  props: ['operator'], // 需要从records中动态取值的字段，最终的结果为title 拼接上从动态字段中取的值，多个字段拼接顺序从左到右
     *  connector: '', // 多个动态字段时，内容之间拼接的连接符，默认为空
     *  items: [{ // 需要展示的项目
     *    label: '操作时间',
     *    prop: 'timestamp', // 从records中动态取值
     *  }, {
     *    label: '构建时长(秒/s)',
     *    prop: 'duration'
     *  }]
     * }
     */
    recordDesc: {
      required: true,
      type: Object
    },

    /**
     * 获取数据，配合滚动分页，需要包含hasMore(Boolean)字段
     */
    fetchApi: {
      type: Function,
      required: true
    },

    // 多条件组合搜索时，表单label的宽度
    labelWidth: {
      type: String,
      default: '100px'
    }
  },
  data() {
    return {
      loadingText: '正在加载数据...',
      hasMore: true, // 是否还有更多数据
      hisLoading: false,
      currentPage: 1,
      searchKeyword: '',
      // 搜索的原始值，比如：下列拉表时，搜索框回填的是label，但实际上搜索需要的是value，记录在此属必事，搜索时使用此字段的值
      originKeyWord: '',
      currentHolderStr: '',

      // 日期时间选择框值
      datePickerVal: {},
      // 下拉列表选择值
      searchSelectVal: {},
      // 文本框输入值
      searchInputVal: {},

      searchProp: '',
      searchPropOptions: [],

      // 默认展示的组件，若不在isDefault为true的组件，则取第一个
      defSearchOption: {},

      // 内容记录
      records: [],

      // 校验相关
      isInvalid: false,
      showErrorMessage: false,
      errorMessage: '',

      // 多条件组合搜索
      multiForm: {},
      multiRules: {},
      isExpanded: false
    }
  },
  watch: {
    searchProp: {
      immediate: true,
      handler(newVal) {
        this.currentHolderStr = this.searchOptions.find(item => item.prop === newVal)?.placeHolder || ''
      }
    },
    recordVisible: {
      immediate: true,
      deep: true,
      handler(newVal) {
        if (newVal) {
          this.initObserver()
        }
      }
    }
  },
  mounted() {
    this.initSysData()
    this.loadInitialData()
  },
  beforeDestroy() {
    this.destroyObserver()
  },
  methods: {
    // 初始化系统数据
    initSysData() {
      // 初始化搜索类型数组
      this.searchOptions.forEach(item => this.searchPropOptions.push({
        label: item.label,
        value: item.prop,
        type: item.type
      }))

      // 设置默认显示搜索类型
      let defaultOp = this.searchOptions.find(item => item.isDefault)
      if (!defaultOp) {
        defaultOp = this.searchOptions[0]
      }
      this.defSearchOption = defaultOp
      this.searchProp = defaultOp.prop

      // 初始化v-model绑定的变量，防止出现undefined的问题
      this.initSelInputVal(1, this.searchInputVal)
      this.initSelInputVal(2, this.datePickerVal)
      this.initSelInputVal(3, this.searchSelectVal)

      // 多条件组合搜索form和rules初始化
      this.searchOptions.forEach(item => {
        this.multiRules[item.prop] = item.rules || []
      })
    },

    initSelInputVal(type, selInputVal) {
      const selInputObjs = this.searchOptions.find(item => item.type === type)
      if (selInputObjs && selInputObjs.length > 0) {
        selInputObjs.forEach(item => {
          selInputVal[item.prop] = ''
        })
      }
    },

    // 处理校验规则
    validateInput() {
      const rules = this.searchOptions.find(item => item.prop === this.searchProp).rules
      if (!rules || rules.length === 0) {
        this.isInvalid = false
        this.showErrorMessage = false
        return
      }
      let valid = true
      for (const r of rules) {
        if (r.required && !this.searchKeyword) {
          this.errorMessage = r.message
          this.isInvalid = true
          this.showErrorMessage = true
          valid = false
          break
        }

        if (r.validator && !r.validator(this.searchKeyword)) {
          this.errorMessage = r.message
          this.isInvalid = true
          this.showErrorMessage = true
          valid = false
          break
        }

        // 正则校验
        if (r.pattern && !r.pattern.test(this.searchKeyword)) {
          this.errorMessage = r.message
          this.isInvalid = true
          this.showErrorMessage = true
          valid = false
          break
        }
      }

      if (valid) {
        this.isInvalid = false
        this.showErrorMessage = false
      }
    },

    // 初始化观察者
    initObserver() {
      this.$nextTick(() => {
        if (this.observer) {
          return
        }
        this.observer = new IntersectionObserver(
          (entries) => {
            const [entry] = entries
            // 当触发元素可见时加载数据
            if (entry.isIntersecting && this.hasMore && !this.isLoading) {
              this.loadMore()
            }
          },
          {
            root: null, // 相对于视口
            rootMargin: '0px', // 不扩展视口
            threshold: 0.1 // 元素可见10%即触发
          }
        )
        // 开始观察触发元素
        this.observer.observe(this.$refs.loaderRef)
      })
    },

    destroyObserver() {
      this.observer?.unobserve(this.$refs.loaderRef)
    },

    // 关闭历史记录操作
    closeBuildHistory() {
      this.$emit('update:recordVisible', false) // 主动通知父组件更新状态
      document.querySelector('.log-container .v-modal').style.display = 'none'
    },

    getDescTitle(record) {
      const title = this.recordDesc.title || '操作标题'
      const props = this.recordDesc.props || []
      const connector = this.recordDesc.connector || ''
      let strLine = ''
      props.forEach(prop => { strLine = strLine + record[prop] + connector })
      if (strLine && strLine.length > 1) {
        strLine = strLine.substring(0, strLine.length)
      }
      return title + strLine
    },

    // 清空内容操作
    clearContext() {
      for (const key in this.searchSelectVal) {
        this.searchSelectVal[key] = ''
      }

      for (const key in this.datePickerVal) {
        this.datePickerVal[key] = ''
      }

      for (const key in this.searchInputVal) {
        this.searchInputVal[key] = ''
      }
    },

    // 处理类型切换
    handleTypeChange() {
      this.showErrorMessage = false
      const complexElDivRefs = this.$refs.complexElDivRef
      // 隐藏与当前属性不匹配的元素
      complexElDivRefs.find(item => {
        if (item.getAttribute('id') === this.searchProp) {
          item.style.display = 'block'
        } else {
          item.style.display = 'none'
        }
      })
      const searchType = this.searchOptions.find(item => item.prop === this.searchProp).type
      setTimeout(() => {
        if (searchType === 2) {
          this.$nextTick(() => {
            this.$refs['datePickerRef-' + this.searchProp][0].focus()
          })
          this.searchPlaceHolder = this.searchOptions.find(item => item.type === 2)?.placeholder
        } else if (searchType === 3) {
          this.$nextTick(() => {
            this.$refs['selectRef-' + this.searchProp][0].toggleMenu()
          })
          this.searchPlaceHolder = this.searchOptions.find(item => item.type === 3)?.placeholder
        } else {
          this.searchPlaceHolder = this.searchOptions.find(item => item.type === 1)?.placeholder
        }
      }, 200)
    },

    // 处理关键字回填
    handleKeyWordDisplay() {
      const searchType = this.searchOptions.find(item => item.prop === this.searchProp).type
      // 处理搜索逻辑
      if (searchType === 1) {
        // 输入框不需要处理
      } else if (searchType === 2) {
        this.searchKeyword = this.datePickerVal[this.searchProp][0] + ' ~ ' + this.datePickerVal[this.searchProp][1]
        this.originKeyWord = this.searchKeyword
      } else {
        const curSelectOptions = this.searchOptions.find(item => item.prop === this.searchProp).options
        this.searchKeyword = curSelectOptions.find(item => item.value === this.searchSelectVal[this.searchProp])['label']
        this.originKeyWord = this.searchSelectVal[this.searchProp]
      }
    },

    // 加载初始数据
    async loadInitialData() {
      this.isLoading = true
      try {
        const res = await this.fetchData()
        // 初始化数据
        this.records = res?.data?.records || []
        // 数据不为空时，默认展示第一条，回调给父组件调用
        if (this.records.length > 0) {
          this.$emit('show-latest', this.records[0])
        }
        this.hasMore = res?.data?.hasMore
        if (!this.hasMore) {
          this.loadingText = '已无更多数据'
        }
      } finally {
        this.isLoading = false
      }
    },

    async fetchData() {
      if (this.multipleSearch) {
        this.$set(this.multiForm, 'current', this.currentPage)
        return await this.fetchApi(this.multiForm)
      }

      const reqParams = {}
      const searchType = this.searchOptions.find(item => item.prop === this.searchProp).type
      this.$set(reqParams, this.searchProp, searchType === 1 ? this.searchKeyword : this.originKeyWord)
      this.$set(reqParams, 'current', this.currentPage)
      return await this.fetchApi(reqParams)
    },

    // 加载更多数据
    async loadMore() {
      this.hisLoading = true
      try {
        this.currentPage++
        const res = await this.fetchData()
        const more = res?.data?.records || []
        if (more.length > 0) {
          more.forEach(item => {
            this.records.push(item)
          })
        }
        this.hasMore = res?.data?.hasMore
        if (!this.hasMore) {
          this.loadingText = '已无更多数据'
        }
      } finally {
        this.hisLoading = false
      }
    },

    // 搜索操作
    handleSearch() {
      this.validateInput() // 提交前再次校验
      if (!this.isInvalid) {
        this.loadInitialData()
      }
    },

    // 重置表单
    resetMultiForm() {
      this.$refs.multiFormRef.resetFields()
    },

    // 查看详情
    lookForDetail(record) {
      this.$emit('update:recordVisible', false) // 主动通知父组件更新状态
      document.querySelector('.log-container .v-modal').style.display = 'none'
      this.$emit('look-for-detail', record)
    },

    /**
     * 重新加载数据，由父组件调用
     */
    reload() {
      this.loadInitialData()
    },

    // 动态获取组件类型
    getComponentType(item) {
      switch (item.type) {
        case 2: return 'el-date-picker'
        case 3: return 'el-select'
        default: return 'el-input'
      }
    },

    // 动态获取组件props
    getComponentProps(item) {
      const baseProps = {
        clearable: true,
        style: { width: '100%' }
      }

      if (item.type === 2) {
        return {
          ...baseProps,
          type: 'datetimerange',
          rangeSeparator: '至',
          startPlaceholder: '开始日期',
          endPlaceholder: '结束日期',
          valueFormat: 'yyyy-MM-dd HH:mm:ss',
          format: 'yyyy-MM-dd HH:mm:ss',
          pickerOptions: item.options || {}
        }
      }

      if (item.type === 3) {
        return {
          ...baseProps,
          placeholder: item.placeHolder
        }
      }

      return {
        ...baseProps,
        placeholder: item.placeHolder,
        size: 'mini'
      }
    }
  }
}
</script>

<style lang="scss" scoped>
  .drawer-container {
    height: 100%;
    display: flex;
    flex-direction: column;
    position: relative;
  }

  .fixed-header {
    background: white; /* 背景色需要与drawer一致 */
    z-index: 1; /* 确保在滚动时覆盖下方内容 */
    padding: 16px;
  }

  .fixed-header .complex-date-picker {
    position: absolute;
    left: 117px;
    top: 0;
    width: 213px;
    visibility: hidden;
  }

  .fixed-header .complex-date-picker .el-input__inner {
    border-radius: 0 !important;
  }

  .el-divider--horizontal {
    margin: 10px 0 !important; /* 调整分割线间距 */
  }

  .scroll-content {
    flex: 1;
    overflow-y: auto;
    padding: 0 16px 16px; /* 下方留出滚动空间 */
  }

  /* 调整滚动条样式（可选） */
  .scroll-content::-webkit-scrollbar {
    width: 6px;
  }

  .scroll-content::-webkit-scrollbar-thumb {
    background: #ddd;
    border-radius: 3px;
  }

  .input-with-select .el-input-group__prepend {
    background-color: #fff;
  }

  .loader-trigger {
    height: 20px;              /* 触发区域高度 */
    width: 100%;
    text-align: center;
  }

  ::v-deep .is-invalid > .el-input__inner {
    border-color: rgb(245, 108, 108) !important;
  }
  .error-msg {
    color: rgb(245, 108, 108);
    margin-left: 102px;
    font-size: 12px;
  }

  ::v-deep .el-drawer__header {
    margin-bottom: 20px !important;
    padding: 15px 15px 0 !important;
  }
</style>
